<template>
  <div class="w-table-wrap" :class="wrapClasses">
    <table class="w-table" :class="classes">
      <thead v-if="!noHeaders">
        <tr>
          <th class="w-table__header" v-for="(header, i) in headers" :key="i"
            @click="header.sortable !== false && sortTable(header)" :class="headerClasses(header)">
            <w-icon class="w-table__header-sort" v-if="header.sortable !== false && header.align === 'right'"
              :class="headerSortClasses(header)">wi-arrow-down</w-icon><template v-if="header.label">
              <slot v-if="$slots['header-label']" name="header-label" :header="header" :label="header.label"
                :index="i + 1">{{ header.label || "" }}</slot><span v-else v-html="header.label || ''"></span>
            </template>
            <w-icon class="w-table__header-sort" v-if="header.sortable !== false && header.align !== 'right'"
              :class="headerSortClasses(header)">wi-arrow-down</w-icon>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr class="w-table__progress-bar" v-if="loading">
          <td :colspan="headers.length">
            <w-progress tile="tile"></w-progress>
            <div class="w-table__loading-text">
              <slot name="loading">Loading...</slot>
            </div>
          </td>
        </tr>
        <template v-else-if="tableItems.length"><template v-for="(item, i) in sortedItems" :key="i">
            <tr class="w-table__row" @click="doSelectRow(item)" :class="{
                'w-table__row--selected': selectedRow === item.uid,
                'w-table__row--expanded': expandedRow === item.uid
              }">
              <template v-for="(header, j) in headers">
                <td class="w-table__cell" v-if="$slots.item" :key="`${j}-1`" :data-label="header.label"
                  :class="`text-${header.align || 'left'}`">
                  <slot name="item" :header="header" :item="item" :label="item[header.key] || ''" :index="i + 1"></slot>
                </td>
                <td class="w-table__cell" v-else :key="`${j}-2`" :data-label="header.label"
                  :class="`text-${header.align || 'left'}`" v-html="item[header.key] || ''"></td>
              </template>
            </tr>
            <w-transition-expand y>
              <tr class="w-table__row" v-if="expandedRow === item.uid">
                <td :colspan="headers.length">
                  <slot name="expanded-row">expanded row</slot>
                </td>
              </tr>
            </w-transition-expand>
          </template></template>
        <tr class="no-data" v-else>
          <td class="w-table__cell text-center" :colspan="headers.length">
            <slot name="no-data">No data to show.</slot>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
  import { ref, computed, watch, getCurrentInstance } from "vue";
  export default {
    name: "w-table",
    props: {
      items: { type: Array, required: true },
      headers: { type: Array, required: true },
      noHeaders: { type: Boolean },
      fixedHeaders: { type: Boolean },
      loading: { type: Boolean },
      // Allow single sort: `+id`, or multiple in an array like: ['+id', '-firstName'].
      sort: { type: [String, Array] },
      selectRow: { type: Boolean },
      expandRow: { type: Boolean },
      filter: { type: Function },
      mobileBreakpoint: { type: Number, default: 0 }
    },

    emits: ["update:sort", "row-select"],
    setup(props, ctx) {
      const instance = getCurrentInstance();
      const activeSorting = ref([]);
      const selectedRow = ref(null);
      const expandedRow = ref(null);

      const tableItems = computed(() => {
        return props.items.map((item, i) => {
          item.uid = i;
          return item;
        });
      });
      const filteredItems = computed(() => {
        if (typeof props.filter === "function")
          return tableItems.value.filter(props.filter);
        return tableItems.value;
      });

      const sortedItems = computed(() => {
        if (!activeSorting.value.length) {
          return filteredItems.value;
        }

        // Only sort with 1 key for now, may handle more later.
        const sortKey1 = activeSorting.value[0].replace(/^[+-]/, "");
        const sortDesc1 = activeSorting.value[0][0] === "-";

        return [...filteredItems.value].sort((a, b) => {
          a = a[sortKey1];
          b = b[sortKey1];
          if (!isNaN(a) && !isNaN(b)) {
            a = parseFloat(a);
            b = parseFloat(b);
          }
          return (a > b ? 1 : -1) * (sortDesc1 ? -1 : 1);
        });
      });

      // Returns an object containing { key1: '+', key2: '-' }. With + or - for ASC/DESC.
      const activeSortingKeys = computed(() => {
        return activeSorting.value.reduce((obj, item) => {
          obj[item.replace(/^[+-]/, "")] = item[0];
          return obj;
        }, {});
      });

      const wrapClasses = computed(() => {
        return {
          "w-table-wrap--loading": props.loading
        };
      });

      const classes = computed(() => {
        return {
          "w-table--mobile": isMobile.value || null,
          "w-table--fixed-header": props.fixedHeaders
        };
      });

      const isMobile = computed(() => {
        return (
          ~~props.mobileBreakpoint &&
          instance.appContext.config.globalProperties.$belleui.breakpoint.width <= ~~props.mobileBreakpoint
        );
      });

      if (!props.sort) {
        activeSorting.value = [];
      } else {
        activeSorting.value = Array.isArray(props.sort)
          ? props.sort
          : [props.sort];
      }

      function headerClasses(header) {
        return {
          "w-table__header--sortable": header.sortable !== false,
          [`text-${header.align || "left"}`]: true
        };
      }

      function headerSortClasses(header) {
        const headerSorting = activeSortingKeys.value[header.key];
        return [
          `w-table__header-sort--${headerSorting ? "active" : "inactive"}`,
          `w-table__header-sort--${headerSorting === "-" ? "desc" : "asc"}`,
          `m${header.align === "right" ? "r" : "l"}1`
        ];
      }

      function sortTable(header) {
        const alreadySortingThis = activeSortingKeys.value[header.key];
        if (alreadySortingThis && activeSortingKeys.value[header.key] === "-") {
          activeSorting.value = [];
          return ctx.emit("update:sort");
        } else
          activeSorting.value[0] = (alreadySortingThis ? "-" : "+") + header.key;

        ctx.emit("update:sort", activeSorting.value);
      }

      function doSelectRow(item) {
        if (props.expandRow) {
          expandedRow.value = item.uid;
          ctx.emit("row-expand", { item, expanded: true });
        } else if (props.selectRow) {
          selectedRow.value = item.uid;
          ctx.emit("row-select", { item });
        }
      }

      watch(
        () => props.sort,
        sorting => {
          if (!sorting) {
            activeSorting.value = [];
          } else {
            activeSorting.value = Array.isArray(sorting) ? sorting : [sorting];
          }
        }
      );

      return {
        activeSorting,
        selectedRow,
        expandedRow,
        // computed
        tableItems,
        filteredItems,
        sortedItems,
        activeSortingKeys,
        wrapClasses,
        classes,
        isMobile,
        headerClasses,
        headerSortClasses,
        sortTable,
        doSelectRow
      };
    }

  };
</script>